#include "TimerGroup.h"

void NahidaProject::HighPrecisionTimer::Start() {
	startTime = std::chrono::high_resolution_clock::now();
	running = true;
}

void NahidaProject::HighPrecisionTimer::Stop() {
	endTime = std::chrono::high_resolution_clock::now();
	running = false;
}

long long NahidaProject::HighPrecisionTimer::ElapsedNanoSeconds() const {
	auto end = running ? std::chrono::high_resolution_clock::now() : endTime;
	return std::chrono::duration_cast<std::chrono::nanoseconds>(end - startTime).count();
}

long long NahidaProject::HighPrecisionTimer::ElapsedMicroSeconds() const {
	return ElapsedNanoSeconds() / 1000;
}

long long NahidaProject::HighPrecisionTimer::ElapsedMilliSeconds() const {
	return ElapsedNanoSeconds() / 1000000;
}

double NahidaProject::HighPrecisionTimer::ElapsedSeconds() const {
	return static_cast<double>(ElapsedNanoSeconds()) / 1000000000.0;
}

void NahidaProject::HighPrecisionTimer::Reset() {
	running = false;
	startTime = std::chrono::high_resolution_clock::time_point();
	endTime = std::chrono::high_resolution_clock::time_point();
}

void NahidaProject::CallbackTimer::Start() {
	if (running) return;

	running = true;
	timerThread = std::thread([this]() {
		while (running) {
			std::unique_lock<std::mutex> lock(mutex);
			if (conditionVariable.wait_for(lock, interval, [this] { return !running; })) {
				break;
			}

			if (running && callback) {
				callback();
			}
		}
	});
}

void NahidaProject::CallbackTimer::Stop() {
	if (!running) return;

	running = false;
	conditionVariable.notify_all();

	if (timerThread.joinable()) {
		timerThread.join();
	}
}

void NahidaProject::CallbackTimer::SetCallback(std::function<void()> callback) {
	callback = callback;
}

void NahidaProject::CallbackTimer::SetInterval(std::chrono::milliseconds interval) {
	interval = interval;
}

void NahidaProject::PrecisionDelay::DelayNanoSeconds(long long nanoseconds) {
	auto start = std::chrono::high_resolution_clock::now();
	auto end = start + std::chrono::nanoseconds(nanoseconds);

	while (std::chrono::high_resolution_clock::now() < end) {
		std::this_thread::yield();
	}
}

void NahidaProject::PrecisionDelay::DelayMicroSeconds(long long microseconds) {
	DelayNanoSeconds(microseconds * 1000);
}

void NahidaProject::PrecisionDelay::DelayMilliSeconds(long long milliseconds) {
	auto start = std::chrono::high_resolution_clock::now();
	auto end = start + std::chrono::milliseconds(milliseconds);

	if (milliseconds > 1) {
		auto sleep_time = std::chrono::milliseconds(milliseconds - 1);
		std::this_thread::sleep_for(sleep_time);
	}

	while (std::chrono::high_resolution_clock::now() < end) {
		std::this_thread::yield();
	}
}

void NahidaProject::PerformanceTimer::Begin() {
	timer.Start();
}

void NahidaProject::PerformanceTimer::End() {
	timer.Stop();
	measurements.push_back(timer.ElapsedSeconds());
}

double NahidaProject::PerformanceTimer::AverageTime() const {
	if (measurements.empty()) return 0.0;
	return std::accumulate(measurements.begin(), measurements.end(), 0.0) / measurements.size();
}

double NahidaProject::PerformanceTimer::MinimumTime() const {
	if (measurements.empty()) return 0.0;
	return *std::min_element(measurements.begin(), measurements.end());
}

double NahidaProject::PerformanceTimer::MaximumTime() const {
	if (measurements.empty()) return 0.0;
	return *std::max_element(measurements.begin(), measurements.end());
}

size_t NahidaProject::PerformanceTimer::Count() const {
	return measurements.size();
}

void NahidaProject::PerformanceTimer::Clear() {
	measurements.clear();
}

void NahidaProject::PerformanceTimer::PrintStatistics() const {
	if (measurements.empty()) {
		std::cout << "No measurements recorded." << std::endl;
		return;
	}

	std::cout << "Performance Statistics:" << std::endl;
	std::cout << "  Count: " << Count() << std::endl;
	std::cout << "  Average: " << AverageTime() * 1000 << " ms" << std::endl;
	std::cout << "  Min: " << MinimumTime() * 1000 << " ms" << std::endl;
	std::cout << "  Max: " << MaximumTime() * 1000 << " ms" << std::endl;
}